package com.cy.utils.dingtalk;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.cy.model.dingtalk.DdConfig;
import com.cy.utils.PropertyUtil;
import com.cy.utils.SimpleHttpUtils;
import com.cy.utils.SimpleHttpUtils.Convertor;
import com.google.common.collect.Lists;

import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Date;
import java.util.Formatter;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * @see {@link http://g.alicdn.com/dingding/opendoc/index.html}
 */
public final class DingTalkUtils {

    private DingTalkUtils() {
    }

    private final static Convertor<String, JSONObject> STRING_TO_JSON_CONVERTOR = new Convertor<String, JSONObject>() {

        @Override
        public JSONObject convert(String source, Object... extra) {
            System.out.println("=============================CCRM==============================");
            System.out.println("request time : " + new Date());
            System.out.println("request url : " + extra[0]);
            if (extra[1] != null){
                StringBuilder requestParams = new StringBuilder("request params : ");
                for (Map.Entry<String, String[]> i : ((Map<String, String[]>)extra[1]).entrySet()){
                    requestParams.append(i.getKey()).append("=");
                    requestParams.append(Arrays.toString(i.getValue()));
                    requestParams.append("  ");
                }
                System.out.println(requestParams);
            }
            System.out.println("return : " + source);
            System.out.println("===============================================================");
            return JSONObject.parseObject(source);
        }
    };

    public final static String AGENT_ID = PropertyUtil.getGrosseValue(PropertyUtil.KERNEL, "agentid");
    public final static String APP_ID = PropertyUtil.getGrosseValue(PropertyUtil.KERNEL, "appid");
    public final static String APP_SECRET = PropertyUtil.getGrosseValue(PropertyUtil.KERNEL, "appsecret");
    public final static String CORP_ID = PropertyUtil.getGrosseValue(PropertyUtil.KERNEL, "corpid");
    public final static String CORP_SECRET = PropertyUtil.getGrosseValue(PropertyUtil.KERNEL, "corpsecret");
    public final static String TOKEN = "123456";
    public final static String ENCODING_AES_KEY = "1234567891234567891234567891234567891234567";
    public final static String JSAPI_NONCE_STR = "abcdefg";

    //有效期2小时，有效期内重复获取自动续期
    private static String accessToken;
    //有效期2小时，有效期内重复获取自动续期
    private static String qrCodeAccessToken;
    static {
        try {
            accessToken = getToken();
            qrCodeAccessToken = getTokenOfQrCode();
        } catch(Exception e){
            e.printStackTrace();
        }
    }

    //扫码登录——获取access_token
    public final static String QR_CODE_GET_TOKEN_IF = "https://oapi.dingtalk.com/sns/gettoken";
    //扫码登录——认证接口
    public final static String QR_CODE_AUTHORIZE_IF = "https://oapi.dingtalk.com/connect/oauth2/sns_authorize";
    //扫码登录——获取持久授权码
    public final static String QR_CODE_GET_PERSISTENT_CODE_IF = "https://oapi.dingtalk.com/sns/get_persistent_code";
    //扫码登录——获取SNS_TOKEN
    public final static String QR_CODE_GET_SNS_TOKEN_IF = "https://oapi.dingtalk.com/sns/get_sns_token";
    //扫码登录——获取当前已登录钉钉的用户
    public final static String QR_CODE_GET_LOGINED_USER_IF = "https://oapi.dingtalk.com/sns/getuserinfo";


    //获取access_token
    public final static String GET_TOKEN_IF = "https://oapi.dingtalk.com/gettoken";
    //获取部门列表
    public final static String GET_DEPTS_IF = "https://oapi.dingtalk.com/department/list";
    //获取部门成员
    public final static String GET_USERS_BY_DEPT_IF = "https://oapi.dingtalk.com/user/list";
    //获取成员详情
    public final static String GET_USER_BY_ID_IF = "https://oapi.dingtalk.com/user/get";
    //获取当前已登录的钉钉用户
    public final static String GET_LOGINED_USER_IF = "https://oapi.dingtalk.com/user/getuserinfo";
    //获取回调接口信息
    public final static String GET_CALLBACK_IF = "https://oapi.dingtalk.com/call_back/get_call_back";
    //删除回调接口
    public final static String DELETE_CALLBACK_IF = "https://oapi.dingtalk.com/call_back/delete_call_back";
    //注册回调接口
    public final static String REGISTER_CALLBACK_IF = "https://oapi.dingtalk.com/call_back/register_call_back";
    //授权登录接口
    public final static String AUTHORIZE_IF = "https://oapi.dingtalk.com/connect/oauth2/authorize";
    //根据unionid获得userid
    public final static String GET_USERID_BY_UNIONID_IF = "https://oapi.dingtalk.com/user/getUseridByUnionid";
    //获取jsapi_ticket
    public final static String GET_JS_API_TICKET_IF = "https://oapi.dingtalk.com/get_jsapi_ticket";

    /**
     * 获取jsapi配置参数
     */
    public static String getJsapiConfig(HttpServletRequest request){
        String urlString = request.getRequestURL().toString();
        String queryString = request.getQueryString();

        String queryStringEncode = null;
        String url;
        if (queryString != null) {
            try {
                queryStringEncode = URLDecoder.decode(queryString, "utf-8");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            url = urlString + "?" + queryStringEncode;
        } else {
            url = urlString;
        }
        long timeStamp = System.currentTimeMillis() / 1000;
        String signedUrl = url;
        String ticket;
        try {
            DdConfig jsapiTicket = DdConfig.dao.getJsapiTicket();
            if (jsapiTicket != null){
                if (jsapiTicket.isUpdatableTicket()){
                    ticket = getJsapiTicket();
                    jsapiTicket.setValue(ticket).updateJsapiTicket();
                } else {
                    ticket = jsapiTicket.getValue();
                }
            } else {
                ticket = getJsapiTicket();
                new DdConfig().setValue(ticket).saveJsapiTicket();
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        String signature = sign(ticket, JSAPI_NONCE_STR, timeStamp, signedUrl);

        JSONObject jsonObject = new JSONObject(6);
        jsonObject.put("jsticket", ticket);
        jsonObject.put("signature", signature);
        jsonObject.put("nonceStr", JSAPI_NONCE_STR);
        jsonObject.put("timeStamp", timeStamp);
        jsonObject.put("corpId", CORP_ID);
        jsonObject.put("agentid", AGENT_ID);

        System.out.println("===========DingTalkUtils.getJsapiConfig()======================");
        System.out.println("request time : " + new Date());
        System.out.println("signed url : " + signedUrl);
        System.out.println("config : " + jsonObject);
        System.out.println("===============================================================");
        return jsonObject.toString();
    }

    /**
     * 获取jsapi ticket
     */
    public static String getJsapiTicket(){
        HashMap<String, String[]> params = new HashMap<>(1, 1);
        params.put("access_token", new String[]{accessToken});
        JSONObject response = SimpleHttpUtils.sendRequest(GET_JS_API_TICKET_IF, "GET", "UTF-8", params, STRING_TO_JSON_CONVERTOR);
        if ("40014".equals(response.getString("errcode"))){
            //token过期
            accessToken = getToken();
            return getJsapiTicket();
        }
        if(response.getIntValue("errcode") == 0){
            return response.getString("ticket");
        }
        return null;
    }

    public static String updateTicket(){
        DdConfig ticket = DdConfig.dao.getJsapiTicket();
        String tic = getJsapiTicket();
        if (ticket != null)
            ticket.setValue(tic).updateJsapiTicket();
        else
            new DdConfig().setValue(tic).saveJsapiTicket();
        return tic;
    }

    /**
     * 签名
     */
    private static String sign(String ticket, String nonceStr, long timeStamp, String url) {
        StringBuilder plain = new StringBuilder();
        plain.append("jsapi_ticket=").append(ticket).append("&noncestr=").append(nonceStr).append("&timestamp=").append(timeStamp).append("&url=").append(url);
        try {
            MessageDigest sha1 = MessageDigest.getInstance("SHA-1");
            sha1.reset();
            sha1.update(plain.toString().getBytes("UTF-8"));
            return bytesToHex(sha1.digest());
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 注册回调接口
     * @return 是否成功
     */
    public static boolean registerCallback(){
        HashMap<String, String[]> params = new HashMap<>(1, 1);
        params.put("access_token", new String[]{accessToken});

        /** 请求体参数
         {
         "call_back_tag": ["user_add_org", "user_modify_org", "user_leave_org"],
         "token": "123456",
         "aes_key": "1",
         "url":"www.dingtalk.com"
         }
         */
        JSONObject requestBody = new JSONObject();
        requestBody.put("call_back_tag", Lists.newArrayList("user_add_org","user_modify_org","user_leave_org"));
        requestBody.put("token", TOKEN);
        requestBody.put("aes_key", ENCODING_AES_KEY);
        requestBody.put("url", PropertyUtil.getGrosseValue(PropertyUtil.KERNEL, "dingTalkEventCallback"));

        String url = REGISTER_CALLBACK_IF +"?access_token="+ accessToken;
        JSONObject response = SimpleHttpUtils.sendRequest(url, "POST", "UTF-8", requestBody.toString(), null, STRING_TO_JSON_CONVERTOR);
        if ("40014".equals(response.getString("errcode"))){
            //token过期
            accessToken = getToken();
            return registerCallback();
        }
        if ("0".equals(response.getString("errcode"))){
            return true;
        }
        return false;
    }

    /**
     * 删除回调接口
     * @return 是否成功
     */
    public static boolean deleteCallback(){
        HashMap<String, String[]> params = new HashMap<>(1, 1);
        params.put("access_token", new String[]{accessToken});
        JSONObject response = SimpleHttpUtils.sendRequest(DELETE_CALLBACK_IF, "GET", "UTF-8", params, STRING_TO_JSON_CONVERTOR);
        if ("40014".equals(response.getString("errcode"))){
            //token过期
            accessToken = getToken();
            return deleteCallback();
        }
        if ("0".equals(response.getString("errcode"))){
            return true;
        }
        return false;
    }

    /**
     * 获取回调接口信息
     * @return
     * {
     *  "aes_key":"1234567891234567891234567891234567891234567",
     *  "call_back_tag":["user_leave_org","user_modify_org","user_add_org"],
     *  "errcode":0,
     *  "errmsg":"ok",
     *  "token":"123456",
     *  "url":"http://150917jq43.iask.in/dingding/testCallback"
     * }
     */
    public static JSONObject getCallback(){
        HashMap<String, String[]> params = new HashMap<>(1, 1);
        params.put("access_token", new String[]{accessToken});
        JSONObject response = SimpleHttpUtils.sendRequest(GET_CALLBACK_IF, "GET", "UTF-8", params, STRING_TO_JSON_CONVERTOR);
        if ("40014".equals(response.getString("errcode"))){
            //token过期
            accessToken = getToken();
            return getCallback();
        }
        if ("0".equals(response.getString("errcode"))){
            return response;
        }
        return null;
    }

    /**
     * 获取当前已登录的钉钉用户
     * @param code 认证code
     * @return
     * {
     *  "errcode": 40029,
     *  "errmsg": "invalid code",
     *  "userid": "USERID",
     *  "deviceId":"DEVICEID",
     *  "is_sys": true,
     *  "sys_level": 0|1|2  级别，三种取值。0:非管理员 1：普通管理员 2：超级管理员
     * }
     */
    public static JSONObject getLoginedUser(String code){
        HashMap<String, String[]> params = new HashMap<>(2, 1);
        params.put("access_token", new String[]{accessToken});
        params.put("code", new String[]{code});
        JSONObject response = SimpleHttpUtils.sendRequest(GET_LOGINED_USER_IF, "GET", "UTF-8", params, STRING_TO_JSON_CONVERTOR);
        if ("40014".equals(response.getString("errcode"))){
            //token过期
            accessToken = getToken();
            return getLoginedUser(code);
        }
        if ("0".equals(response.getString("errcode"))){
            return response;
        }
        return null;
    }

    /**
     * 获取所有成员
     * @param deptId
     * @return
     * [{
     *  "active": true,
     *  "avatar": "",
     *  "department": [1615377,1618357,3500851,3504994],
     *  "dingId": "$:LWCP_v1:$bbZyZnOSxBkSGlXwxjC7iQ==",
     *  "email": "xx@callbell.cn",
     *  "isAdmin": true,
     *  "isBoss": true,
     *  "isHide": false,
     *  "isLeader": true,
     *  "jobnumber": "",
     *  "mobile": "18925125199",
     *  "name": "xx",
     *  "openId": "ggRKiiFsiiceoiE",
     *  "order": ​188267159930443932,
     *  "position": "副总经理",
     *  "remark": "",
     *  "tel": "",
     *  "userid": "cb-003",
     *  "workPlace": "广州"
     * }]
     */
    public static Set<JSONObject> getAllUsers(){
        JSONArray depts = getAllDepts();
        Set<JSONObject> distinctUsers = new HashSet<>();
        for (Object d : depts){
            JSONObject dept = (JSONObject) d;
            String deptId = dept.getString("id");
            JSONArray users = getUsersByDept(deptId);
            for (Object u : users){
                JSONObject user = new JSONObject((JSONObject) u){
                    @Override
                    public int hashCode() {
                        return getString("userid").hashCode();
                    }

                    @Override
                    public boolean equals(Object obj) {
                        if (obj == null)
                            return false;
                        if (this == obj)
                            return true;
                        if (obj instanceof JSONObject){
                            JSONObject o = (JSONObject) obj;
                            return getString("userid").equals(o.getString("userid"));
                        }
                        return false;
                    }
                };
                distinctUsers.add(user);
            }
        }
        return distinctUsers;
    }

    /**
     * 获取成员详情
     * @param userId
     * @return
     * {
     *  "errcode": 0,
     *  "errmsg": "ok",
     *  "userid": "zhangsan",
     *  "name": "张三",
     *  "tel" : "010-123333",
     *  "workPlace" :"",
     *  "remark" : "",
     *  "mobile" : "13800000000",
     *  "email" : "dingding@aliyun.com",
     *  "active" : true,
     *  "orderInDepts" : "{1:10, 2:20}",
     *  "isAdmin" : false,
     *  "isBoss" : false,
     *  "dingId" : "WsUDaq7DCVIHc6z1GAsYDSA",
     *  "isLeaderInDepts" : "{1:true, 2:false}",
     *  "isHide" : false,
     *  "department": [1, 2],
     *  "position": "工程师",
     *  "avatar": "dingtalk.com/abc.jpg",
     *  "jobnumber": "111111",
     *  "extattr": {
     *      "爱好":"旅游",
     *      "年龄":"24"
     *  }
     * }
     */
    public static JSONObject getUserById(String userId){
        HashMap<String, String[]> params = new HashMap<>(2, 1);
        params.put("access_token", new String[]{accessToken});
        params.put("userid", new String[]{userId});
        JSONObject response = SimpleHttpUtils.sendRequest(GET_USER_BY_ID_IF, "GET", "UTF-8", params, STRING_TO_JSON_CONVERTOR);
        if ("40014".equals(response.getString("errcode"))){
            //token过期
            accessToken = getToken();
            return getUserById(userId);
        }
        if ("0".equals(response.getString("errcode"))){
            return response;
        }
        return null;
    }

    /**
     * 获取部门成员
     * @param deptId
     * @return
     * [{
     *  "active": true,
     *  "avatar": "",
     *  "department": [1615377,1618357,3500851,3504994],
     *  "dingId": "$:LWCP_v1:$bbZyZnOSxBkSGlXwxjC7iQ==",
     *  "email": "xx@callbell.cn",
     *  "isAdmin": true,
     *  "isBoss": true,
     *  "isHide": false,
     *  "isLeader": true,
     *  "jobnumber": "",
     *  "mobile": "18925125199",
     *  "name": "xx",
     *  "openId": "ggRKiiFsiiceoiE",
     *  "order": ​188267159930443932,
     *  "position": "副总经理",
     *  "remark": "",
     *  "tel": "",
     *  "userid": "cb-003",
     *  "workPlace": "广州"
     * }]
     */
    public static JSONArray getUsersByDept(String deptId){
        HashMap<String, String[]> params = new HashMap<>(2, 1);
        params.put("access_token", new String[]{accessToken});
        params.put("department_id", new String[]{deptId});
        JSONObject response = SimpleHttpUtils.sendRequest(GET_USERS_BY_DEPT_IF, "GET", "UTF-8", params, STRING_TO_JSON_CONVERTOR);
        if ("40014".equals(response.getString("errcode"))){
            //token过期
            accessToken = getToken();
            return getUsersByDept(deptId);
        }
        if ("0".equals(response.getString("errcode"))){
            return response.getJSONArray("userlist");
        }
        return null;
    }

    /**
     * 获取所有部门
     * @return
     * [{
     *  "createDeptGroup": true,
     *  "name": "xxx有限公司",
     *  "id": ​1,
     *  "autoAddUser": true
     * }]
     */
    public static JSONArray getAllDepts(){
        HashMap<String, String[]> params = new HashMap<>(1, 1);
        params.put("access_token", new String[]{accessToken});
        JSONObject response = SimpleHttpUtils.sendRequest(GET_DEPTS_IF, "GET", "UTF-8", params, STRING_TO_JSON_CONVERTOR);
        if ("40014".equals(response.getString("errcode"))){
            //token过期
            accessToken = getToken();
            return getAllDepts();
        }
        if ("0".equals(response.getString("errcode"))){
            return response.getJSONArray("department");
        }
        return null;
    }

    /**
     * 根据unionId获得userId
     * @return userid
     */
    public static String getUseridByUnionid(String unionId){
        HashMap<String, String[]> params = new HashMap<>(2, 1);
        params.put("access_token", new String[]{accessToken});
        params.put("unionid", new String[]{unionId});
        JSONObject response = SimpleHttpUtils.sendRequest(GET_USERID_BY_UNIONID_IF, "GET", "UTF-8", params, STRING_TO_JSON_CONVERTOR);
        if ("40014".equals(response.getString("errcode"))){
            //token过期
            accessToken = getToken();
            return getUseridByUnionid(unionId);
        }
        if(response.getIntValue("errcode") == 0){
            return response.getString("userid");
        }
        return null;
    }

    /**
     * 获取access_token
     * @return access token
     */
    public static String getToken(){
        HashMap<String, String[]> params = new HashMap<>(2, 1);
        params.put("corpid", new String[]{CORP_ID});
        params.put("corpsecret", new String[]{CORP_SECRET});
        JSONObject response = SimpleHttpUtils.sendRequest(GET_TOKEN_IF, "GET", "UTF-8", params, STRING_TO_JSON_CONVERTOR);
        if(response.getIntValue("errcode") == 0){
            return response.getString("access_token");
        }
        return null;
    }

    /**
     * 获取扫码登录的access_token
     */
    public static String getTokenOfQrCode(){
        HashMap<String, String[]> params = new HashMap<>(2, 1);
        params.put("appid", new String[]{APP_ID});
        params.put("appsecret", new String[]{APP_SECRET});
        JSONObject response = SimpleHttpUtils.sendRequest(QR_CODE_GET_TOKEN_IF, "GET", "UTF-8", params, STRING_TO_JSON_CONVERTOR);
        if(response.getIntValue("errcode") == 0){
            return response.getString("access_token");
        }
        return null;
    }

    /**
     * 获取扫码登录的持久授权码
     * @param 授权码
     * @return
     * {
     *  "errcode": 0,
     *  "errmsg": "ok",
     *  "openid": "liSii8KCxxxxx",
     *  "persistent_code": "dsa-d-asdasdadHIBIinoninINIn-ssdasd",
     *  "unionid": "7Huu46kk"
     * }
     */
    public static JSONObject getPersistentCodeOfQrCode(String code){
        HashMap<String, String[]> params = new HashMap<>(1, 1);
        params.put("access_token", new String[]{qrCodeAccessToken});

        //请求体参数{"tmp_auth_code": "23152698ea18304da4d0ce1xxxxx"}
        JSONObject requestBody = new JSONObject();
        requestBody.put("tmp_auth_code", code);

        String url = QR_CODE_GET_PERSISTENT_CODE_IF +"?access_token="+ qrCodeAccessToken;
        JSONObject response = SimpleHttpUtils.sendRequest(url, "POST", "UTF-8", requestBody.toString(), null, STRING_TO_JSON_CONVERTOR);
        if ("40014".equals(response.getString("errcode"))){
            //token过期
            qrCodeAccessToken = getTokenOfQrCode();
            return getPersistentCodeOfQrCode(code);
        }
        if ("0".equals(response.getString("errcode"))){
            return response;
        }
        return null;
    }

    /**
     * 获取sns_token
     * @param 授权码
     * @return
     * {
     *  "errcode": 0,
     *  "errmsg": "ok",
     *  "expires_in": 7200,
     *  "sns_token": "c76dsc87ds6c876sd87csdcxxxxx"
     * }
     */
    public static String getSnsToken(String code){
        JSONObject result = getPersistentCodeOfQrCode(code);
        if (result == null)
            return null;

        HashMap<String, String[]> params = new HashMap<>(1, 1);
        params.put("access_token", new String[]{qrCodeAccessToken});

        //请求体参数{"openid": "liSii8KCxxxxx","persistent_code": "dsa-d-asdasdadHIBIinoninINIn-ssdasd"}
        JSONObject requestBody = new JSONObject();
        requestBody.put("openid", result.getString("openid"));
        requestBody.put("persistent_code", result.getString("persistent_code"));

        String url = QR_CODE_GET_SNS_TOKEN_IF +"?access_token="+ qrCodeAccessToken;
        JSONObject response = SimpleHttpUtils.sendRequest(url, "POST", "UTF-8", requestBody.toString(), null, STRING_TO_JSON_CONVERTOR);
        if ("40014".equals(response.getString("errcode"))){
            //token过期
            qrCodeAccessToken = getTokenOfQrCode();
            return getSnsToken(code);
        }
        if ("0".equals(response.getString("errcode"))){
            return response.getString("sns_token");
        }
        return null;
    }

    /**
     * 获取当前已登录的钉钉用户
     * @param 授权码
     * @return
     * {
     *  "corp_info": [
     *      {
     *          "corp_name": "阿里巴巴",
     *          "is_auth": true,
     *          "is_manager": false,
     *          "rights_level": 100
     *      },{
     *          "corp_name": "DingTalk",
     *          "is_auth": true,
     *          "is_manager": false,
     *          "rights_level": 200
     *  }],
     *  "errcode": 0,
     *  "errmsg": "ok",
     *  "user_info": {
     *      "maskedMobile": "130****1234",
     *      "nick": "张三",
     *      "openid": "liSii8KCxxxxx",
     *      "unionid": "7Huu46kk",
     *      "dingId": "dingId"
     *  }
     * }
     */
    public static JSONObject getLoginedUserOfQrCode(String code){
        HashMap<String, String[]> params = new HashMap<>(2, 1);
        params.put("sns_token", new String[]{getSnsToken(code)});
        JSONObject response = SimpleHttpUtils.sendRequest(QR_CODE_GET_LOGINED_USER_IF, "GET", "UTF-8", params, STRING_TO_JSON_CONVERTOR);
        if ("40014".equals(response.getString("errcode"))){
            //sns_token过期
            return getSnsToken(code) == null ? null : getLoginedUserOfQrCode(code);
        }
        if ("0".equals(response.getString("errcode"))){
            return response;
        }
        return null;
    }

    private static String bytesToHex(byte[] hash) {
        Formatter formatter = new Formatter();
        for (byte b : hash) {
            formatter.format("%02x", b);
        }
        String result = formatter.toString();
        formatter.close();
        return result;
    }

}